#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "qmath.h"
#include "QFileDialog"
#include "QMessageBox"
#include <QDebug>
#include <time.h>

MainWindow::MainWindow(QApplication *pApp, QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    m_N_inputs(28),
    b_ActualTrainingSet(false)
{
    m_pApp = pApp;

    ui->setupUi(this);

    srand(time(0)); // randomize

    connect(this->ui->action_saveFile_2, SIGNAL(triggered()), SLOT(on_action_saveFile()));
    connect(this->ui->action_openFile_2, SIGNAL(triggered()), SLOT(on_action_openFile()));
    connect(this->ui->action_saveSampleFile, SIGNAL(triggered()), SLOT(on_action_saveSampleFile()));
    connect(this->ui->action_openSampleFile, SIGNAL(triggered()), SLOT(on_action_openSampleFile()));
    connect(&this->net, SIGNAL(net.needToUpdateForm()), this, SLOT(needToUpdateForm()));

    m_pInputs = new int[m_N_inputs*m_N_inputs]; // 28 x 1
    for(int i =0; i < m_N_inputs*m_N_inputs; i++)
        *(m_pInputs+i) = 0;

    /* // log-файл
    m_logFile = new QFile("log.txt");
    if(m_logFile->open(QIODevice::Append | QIODevice::Text))
    {
        m_log = new QTextStream(m_logFile);        
    }
    */

   /* for(int i = 0; i < 4; i++)
    {
        QString neuronName;
        neuronName.sprintf("Neuron%d", i);
        ui->listWidget_Neurons->addItem(neuronName);
        m_Neurons[i].Init(NULL, 28);
        m_Neurons[i].setName(&neuronName);
        *m_log << m_Neurons[i].PrintState() << endl;
    }*/    
}

MainWindow::~MainWindow()
{
    //if(m_log) delete m_log;
    delete ui;    
}

void MainWindow::on_comboBox_N_inputs_currentIndexChanged(int index)
{
    m_N_inputs = index + 28;
    ui->tableWidget->clear();

  /*  for(int i = 0; i < 4; i++)
    {
        m_Neurons[i].Init(NULL, m_N_inputs);
        *m_log << m_Neurons[i].PrintState() << endl;
    }*/

    delete[] m_pInputs;
    m_pInputs = new int[m_N_inputs];
    for(int i =0; i < m_N_inputs; i++)
        *(m_pInputs+i) = 0;
    ui->tableWidget->setColumnCount(m_N_inputs);
    ui->tableWidget->setRowCount(m_N_inputs);
}

template <typename T>
bool openSample(QString fileName, int* _N_inputs, T* pInputs, int* pTargetNumber)
/*
 * ф-я чтения файла образца
 * fileName - имя файла
 * _N_inputs - куда пишется кол-во точек образца
 * pInputs - куда пишутся значения точек
 * pTargetNumber - куда пишется номер классифицируемого образца текущего образа
 */
{
    QFile file(fileName);
    if(file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&file);
        int N_inputs;
        in >> N_inputs;
        in >> *pTargetNumber;

        if(N_inputs < 0 || N_inputs > 10000)
        {
            QMessageBox mb;
            mb.setWindowTitle("Error");
            mb.setText("error sample file!");
            mb.exec();
            return 0;
        }
        else{
            if(pInputs)
                delete[] pInputs;

            *_N_inputs = sqrt(N_inputs);

            pInputs = new int[N_inputs];

            for(int i = 0; i < N_inputs; i++)
            {
                int value;
                in >> value;
                *(pInputs+i) = (T)value;
            }

            return 1;
        }
    }
    return 0;
}

void MainWindow::on_tableWidget_cellClicked(int row, int column)
{
    QTableWidgetItem* new_item = new QTableWidgetItem();
    int* pInp = (m_pInputs + row*m_N_inputs + column);
    if(*pInp != 1)
    {
        new_item->setBackgroundColor(QColor(0, 0, 0));
        *pInp = 1;
    }
    else {
        new_item->setBackgroundColor(QColor(255, 255, 255));
        *pInp = 0;
    }
    ui->tableWidget->setItem(row, column, new_item);
}

void MainWindow::updateField() const
{
    ui->tableWidget->clear();

    ui->tableWidget->setRowCount(m_N_inputs);
    ui->tableWidget->setColumnCount(m_N_inputs);

    for(int row = 0; row < m_N_inputs; row++)
    {
        for(int column = 0; column < m_N_inputs; column++)
        {
            QTableWidgetItem* new_item = new QTableWidgetItem();
            int* pInp = (m_pInputs + row*m_N_inputs + column);

            if(*pInp == 0)
            {
                new_item->setBackgroundColor(QColor(255, 255, 255));
            }
            else
            {
                new_item->setBackgroundColor(QColor(0, 0, 0));
            }

            ui->tableWidget->setItem(row, column, new_item);
        }
    }
}

void MainWindow::updateTrainingSet() // обновление обуч. выборки
{
    // чтение training_set.txt
    vector<QString> files = getTrainingSamples();

    if(files.empty())
    {
        qDebug() << "training_set.txt is empty!" << endl;
        QMessageBox mb;
        mb.setWindowTitle("Error");
        mb.setText("Список обучающей выборки пуст! Повторите выбор образцов выборки, для этого нажмите \"Выборать обуч. выборку\" ...");
        mb.exec();
        return;
    }
    else
    {
        vector<QString>::iterator it = files.begin();
        while(it != files.end())
        {
            qDebug() << *it;
            it++;
        }
    }

    // читаем каждый файл образца из выборки, сохраняем матрицу и ответ в вектор
    vector<QString>::iterator iter = files.begin();
    while(iter != files.end())
    {
        int target, N_inputs, *pInputs = new int[28*28];
        if(openSample<int>(*iter, &N_inputs, pInputs, &target))
        {
            matrix newMatrix = fieldToMatrix(N_inputs*N_inputs, pInputs);
            trainingSet.push_back(pair<matrix, int>(newMatrix, target));
            delete[] pInputs;
        }
        iter++;
    }

    b_ActualTrainingSet = true;
}


vector<QString> MainWindow::getTrainingSamples()
{ // получение обучающей выборки (образцов)
    vector<QString> files;
    QFile file("training_set.txt");

    if(file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&file);
        while(!in.atEnd())
        {
            QString file;
            file = in.readLine();
            files.push_back(file);
        }
    }

    return files;
}

void MainWindow::on_pushButton_RunEra_clicked()
{
    // тут работа без обучения сети
    matrix newMatrix = fieldToMatrix(m_N_inputs*m_N_inputs, m_pInputs);
    vector<matrix> input;
    input.push_back(newMatrix);
    vector<int> answer = net.calculation(input);
    QString message, text;
    for(vector<int>::iterator iter = answer.begin(); iter != answer.end(); iter++)
    {
        message.sprintf("answer = %d\n", *iter);
        text += message;
    }

    QMessageBox mb;
    mb.setText(text);
    mb.exec();
}

void MainWindow::on_action_saveFile()
{
    // сохранение состояния сети
    /*
    QFileDialog dg;
    QString fileName = dg.getSaveFileName();
    QFile file(fileName);
    if(file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream out(&file);
        out << saveState();
        out.flush();
    }
    */
}

void MainWindow::on_action_openFile()
{
    // открытие состояния сети
    /*
    QFileDialog dg;
    QString fileName = dg.getOpenFileName();
    QFile file(fileName);
    if(file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&file);
        int N_neurons, N_inputs;
        in >> N_neurons >> N_inputs;
        if(N_inputs < 0 || N_inputs > 10000)
        {
            QMessageBox mb;
            mb.setWindowTitle("Error");
            mb.setText("error file!");
            mb.exec();
        }
        else{
            ui->comboBox_N_inputs->setCurrentIndex(N_inputs-28);

            for(int j = 0; j < N_neurons; j++)
            {
                int era;
                float* pW = new float[N_inputs+1];
                in >> era >> *pW;
                for(int i = 0; i < N_inputs; i++)
                {
                    in >> *(pW+i+1);
                }
                m_Neurons[j].Init(pW, N_inputs, era);

                QString name;
                in >> name;
                m_Neurons[j].setName(&name);
                QListWidgetItem* p = ui->listWidget_Neurons->item(j);
                p->setText(name);
            }
        }
    }
    */
}

QString MainWindow::saveState()
{
    QString result;

    int N_inputs = ui->comboBox_N_inputs->currentIndex() + 28;
    N_inputs *= N_inputs;

    result.sprintf("%d %d\n", 4, N_inputs);

 /*   for(int i = 0; i < 4; i++)
    {
        result += m_Neurons[i].saveNeuron();
    }*/

    return result;
}

void MainWindow::on_action_saveSampleFile()
{
    QFileDialog dg;
    QString fileName = dg.getSaveFileName();
    QFile file(fileName);
    if(file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream out(&file);

        bool b = false;
        int target_number = ui->lineEdit_targetNumber->text().toInt(&b);

        if(!b)
        {
            QMessageBox mb;
            mb.setWindowTitle("Error");
            mb.setText("error with conversion targetNuber to int value!");
            mb.exec();
            return;
        }

        out << m_N_inputs*m_N_inputs << ' ' << target_number << endl; // в 1й строке число точек в образце и номер распознаваемого образа (ответ)

        for(int i =0; i < m_N_inputs*m_N_inputs; i++) // пишем значения точек через пробел
            out << *(m_pInputs+i) << ' ';

        out.flush();
    }
}

void MainWindow::on_action_openSampleFile()
{
    QFileDialog dg;
    QString fileName = dg.getOpenFileName();
    int targetNubmer;

    openSample<int>(fileName, &m_N_inputs, m_pInputs, &targetNubmer);

    QString s;
    s.sprintf("%d", targetNubmer);
    ui->lineEdit_targetNumber->setText(s);

    updateField();
}

void MainWindow::on_listWidget_Neurons_doubleClicked(const QModelIndex &index)
{
 /*   m_Neurons[ui->listWidget_Neurons->currentRow()].getInfo();*/
}

void MainWindow::on_pushButton_getTrainingSet_clicked()
{ // выбор файлов для обуч. выборки
    QFileDialog dg;
    QStringList fileNames = dg.getOpenFileNames();

    QFile file("training_set.txt");

    if(file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream out(&file);
        QStringList::Iterator iter = fileNames.begin();
        while(iter != fileNames.end())
        {
            out << *iter << endl;
            iter++;
        }
    }

    b_ActualTrainingSet = false;
}

void MainWindow::on_pushButton_clicked()
{
    if(!b_ActualTrainingSet)
        updateTrainingSet();

    net.run_era_with_histograms(trainingSet);
}

void MainWindow::needToUpdateForm()
{
    m_pApp->processEvents(QEventLoop::ExcludeUserInputEvents);
}


void MainWindow::on_tableWidget_cellEntered(int row, int column)
{
    QTableWidgetItem* new_item = new QTableWidgetItem();
    int* pInp = (m_pInputs + row*m_N_inputs + column);
    if(*pInp != 1)
    {
        new_item->setBackgroundColor(QColor(0, 0, 0));
        *pInp = 1;
    }
    else {
        new_item->setBackgroundColor(QColor(255, 255, 255));
        *pInp = 0;
    }
    ui->tableWidget->setItem(row, column, new_item);
}

void MainWindow::on_pushButton_2_clicked()
{
    ui->tableWidget->clear();
    for(int i =0; i < m_N_inputs*m_N_inputs; i++) // пишем значения точек через пробел
        *(m_pInputs+i) = 0;
}

void MainWindow::on_pushButton_persLearning_clicked()
{
    // запуск обучения : подавать на нейросеть сохранённый набор образов для обучения из файла training_set.txt
    if(!b_ActualTrainingSet)
        updateTrainingSet();

    net.learning(trainingSet);
    qDebug() << "Pers. learning is ok!" << endl;
}

void MainWindow::on_pushButton_convLearning_clicked()
{
    // запуск обучения : подавать на нейросеть сохранённый набор образов для обучения из файла training_set.txt

    if(!b_ActualTrainingSet)
        updateTrainingSet();

    net.learning_new(trainingSet);
}
